package com.gusto.engine.colfil.matrix.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.log4j.Logger;

import com.gusto.engine.colfil.Prediction;
import com.gusto.engine.colfil.Rating;
import com.gusto.engine.colfil.matrix.MatrixDAO;
import com.opensymphony.oscache.base.Cache;

/**
 * <p>Cached implementation of {@link MatrixDAO}.</p>
 * 
 * @author amokrane.belloui@gmail.com
 * 
 */
public class MatrixDAOCachedImpl implements MatrixDAO {
	
	private Logger log = Logger.getLogger(getClass());
	
	private Cache cache = null;
	public void setCache(Cache cache) {
		this.cache = cache;
	}
	
	private MatrixDAO matrixDAO;
	public void setMatrixDAO(MatrixDAO matrixDAO) {
		this.matrixDAO = matrixDAO;
	}
	
	public double getUserMeanRating(long userId) {
		double mean;
		String key = "user.rating.mean." + userId;
		try {
			mean = (Double) cache.getFromCache(key);
			log.debug("Get User Mean Rating from cache " + key);
		} 
		catch (com.opensymphony.oscache.base.NeedsRefreshException nre) {
		    try {
		    	mean = matrixDAO.getUserMeanRating(userId); 
		        cache.putInCache(key, mean);
		        log.debug("Get User Mean Rating " + key);
		    } 
		    catch (Exception ex) {
		    	mean = (Double) nre.getCacheContent();
		        cache.cancelUpdate(key);
		        log.debug("Get User Mean Rating from cache (Exception) " + key);
		    }
		}
	    return mean;
	}
	
	public double getItemMeanRating(long itemId) {
		double mean;
		String key = "item.rating.mean." + itemId;
		try {
			mean = (Double) cache.getFromCache(key);
			log.debug("Get Item Mean Rating from cache" + key);
		} 
		catch (com.opensymphony.oscache.base.NeedsRefreshException nre) {
		    try {
		    	mean = matrixDAO.getItemMeanRating(itemId);
		        cache.putInCache(key, mean);
		        log.debug("Get Item Mean Rating " + key);
		    } 
		    catch (Exception ex) {
		    	mean = (Double) nre.getCacheContent();
		        cache.cancelUpdate(key);
		        log.debug("Get Item Mean Rating from cache (Exception)" + key);
		    }
		}
	    return mean;
	}
	
	@SuppressWarnings("unchecked")
	public List<Rating> getUsersHavingRatedItem(long itemId, boolean normalized) {
		List<Rating> evals;
		String key = "item.ratings.user." + itemId;
		try {
			evals = (List<Rating>) cache.getFromCache(key);
			//log.debug("Get Users Having Rated Item from cache " + key);
		} 
		catch (com.opensymphony.oscache.base.NeedsRefreshException nre) {
		    try {
		    	evals = matrixDAO.getUsersHavingRatedItem(itemId, normalized); 
		        cache.putInCache(key, evals);
		        //log.debug("Get Users Having Rated Item " + key);
		    } 
		    catch (Exception ex) {
		    	evals = (List<Rating>) nre.getCacheContent();
		        cache.cancelUpdate(key);
		        //log.debug("Get Users Having Rated Item from cache (Exception) " + key);
		    }
		}
	    return evals;
	}
	
	@SuppressWarnings("unchecked")
	public List<Rating> getItemsRatedByUser(long userId, boolean normalized) {
		List<Rating> evals;
		String key = "user.ratings.item." + userId;
		try {
			evals = (List<Rating>)cache.getFromCache(key);
			//log.debug("Get Item Rated by User from cache " + key);
		} 
		catch (com.opensymphony.oscache.base.NeedsRefreshException nre) {
		    try {
		    	evals = matrixDAO.getItemsRatedByUser(userId, normalized); 
		        cache.putInCache(key, evals);
		        //log.debug("Get Item Rated by User " + key);
		    } 
		    catch (Exception ex) {
		    	evals = (List<Rating>)nre.getCacheContent();
		        cache.cancelUpdate(key);
		        //log.debug("Get Item Rated by User from cache (Exception)" + key);
		    }
		}
	    return evals;
	}
	
	public Rating getRating(long userId, long itemId) {
		return matrixDAO.getRating(userId, itemId);
	}
	
	public Prediction getPrediction(long userId, long itemId) {
		return matrixDAO.getPrediction(userId, itemId);
	}
	
	public List<Rating> getSubMatrix(Collection<Long> usersIds, Collection<Long> itemsIds) {
		//log.debug("Getting cached SubMatrix");
		List<Rating> ratings = new ArrayList<Rating>();
		for (Long i : usersIds) {
			// Time consuming
			// FIXME Normalized or not
			List<Rating> urats = this.getItemsRatedByUser(i, false);
			
			if (urats != null) {
				for (Rating rating : urats) {
					if (itemsIds.contains(rating.getItemId())) {
						ratings.add(rating);
					}
				}
			}
		}
		return ratings;
		// normal Method
		//return matrixDAO.getSubMatrix(usersIds, itemsIds);
	}
	
	
	public void insertPrediction(long userId, long itemId, Prediction prediction) {
		matrixDAO.insertPrediction(userId, itemId, prediction);
	}
	
	public void insertRating(long userId, long itemId, Rating rating, boolean checkExistence) {
		matrixDAO.insertRating(userId, itemId, rating, checkExistence);
	}
	
}
